package cn.aicnn.chatssespringboot.service.test;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.*;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.ast.expr.*;
import com.github.javaparser.ast.stmt.*;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;

import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

public class JavaProjectMetricsAnalyzer {

    private static class ProjectMetrics {
        List<FileMetrics> fileMetrics = new ArrayList<>();

        int totalLines = 0;
        int totalCommentLines = 0;
        int totalCyclomaticComplexity = 0;

        void addFileMetrics(FileMetrics fm) {
            fileMetrics.add(fm);
            totalLines += fm.linesOfCode;
            totalCommentLines += fm.commentLines;
            totalCyclomaticComplexity += fm.cyclomaticComplexity;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("=== 项目总体分析 ===\n");
            sb.append("项目文件总数: ").append(fileMetrics.size()).append("\n");
            sb.append("总代码行数: ").append(totalLines).append("\n");
            sb.append("总注释行数: ").append(totalCommentLines).append("\n");
            sb.append("总圈复杂度: ").append(totalCyclomaticComplexity).append("\n");
            double density = totalLines == 0 ? 0 : (double) totalCommentLines / totalLines * 100;
            sb.append("平均注释密度: ").append(String.format("%.2f", density)).append("%\n\n");

            sb.append("=== 每个文件分析 ===\n\n");
            for (FileMetrics fm : fileMetrics) {
                sb.append(fm).append("\n\n");
            }
            return sb.toString();
        }
    }

    private static class FileMetrics {
        String fileName;
        int linesOfCode;
        int commentLines;
        int cyclomaticComplexity;

        int externalInputs = 0;
        int externalOutputs = 0;
        int externalQueries = 0;
        int internalLogicalFiles = 1;
        int externalInterfaceFiles = 1;

        int wmc = 0;
        int dit = 1;
        int noc = 0;
        int cbo = 0;
        int rfc = 0;
        int lcom = 0;

        int useCasePoints = 0;
        int actors = 1;
        int useCases = 0;

        double commentDensity = 0;

        @Override
        public String toString() {
            return "文件: " + fileName + "\n\n" +
                    "=== 功能点度量 ===\n" +
                    "外部输入数(EI): " + externalInputs + "\n" +
                    "外部输出数(EO): " + externalOutputs + "\n" +
                    "外部查询数(EQ): " + externalQueries + "\n" +
                    "内部逻辑文件(ILF): " + internalLogicalFiles + "\n" +
                    "外部接口文件(EIF): " + externalInterfaceFiles + "\n\n" +

                    "=== CK度量 ===\n" +
                    "加权方法数(WMC): " + wmc + "\n" +
                    "继承树深度(DIT): " + dit + "\n" +
                    "子类数(NOC): " + noc + "\n" +
                    "类间耦合度(CBO): " + cbo + "\n" +
                    "类的响应集合(RFC): " + rfc + "\n" +
                    "方法内聚缺乏度(LCOM): " + lcom + "\n\n" +

                    "=== LK度量 ===\n" +
                    "圈复杂度: " + cyclomaticComplexity + "\n" +
                    "代码行数: " + linesOfCode + "\n" +
                    "注释行数: " + commentLines + "\n" +
                    "注释密度(%): " + String.format("%.2f", commentDensity) + "\n\n" +

                    "=== 用例图度量 ===\n" +
                    "用例点(UCP): " + useCasePoints + "\n" +
                    "参与者数: " + actors + "\n" +
                    "用例数: " + useCases;
        }
    }

    public static void main(String[] args) throws Exception {
        String projectRoot = "D:\\grade3\\intership\\Thermal_Inspection_cloud"; // 项目路径
        ProjectMetrics metrics = analyzeProject(projectRoot);
        System.out.println(metrics);
    }

    public static ProjectMetrics analyzeProject(String projectRoot) throws Exception {
        ProjectMetrics projectMetrics = new ProjectMetrics();
        Files.walk(Paths.get(projectRoot))
                .filter(p -> p.toString().endsWith(".java"))
                .forEach(p -> {
                    try {
                        FileMetrics fm = analyzeJavaFile(p.toFile());
                        projectMetrics.addFileMetrics(fm);
                    } catch (Exception e) {
                        System.err.println("分析出错: " + p + " - " + e.getMessage());
                    }
                });
        return projectMetrics;
    }

    private static FileMetrics analyzeJavaFile(File javaFile) throws Exception {
        FileMetrics metrics = new FileMetrics();
        metrics.fileName = javaFile.getName();
        List<String> lines = Files.readAllLines(javaFile.toPath());
        metrics.linesOfCode = lines.size();

        JavaParser parser = new JavaParser();
        FileInputStream in = new FileInputStream(javaFile);
        ParseResult<CompilationUnit> result = parser.parse(in);

        if (result.isSuccessful() && result.getResult().isPresent()) {
            CompilationUnit cu = result.getResult().get();
            List<Comment> comments = cu.getAllContainedComments();
            metrics.commentLines = comments.size();
            metrics.commentDensity = metrics.linesOfCode == 0 ? 0 :
                    (double) metrics.commentLines / metrics.linesOfCode * 100;

            Set<String> calledMethods = new HashSet<>();
            Set<String> accessedTypes = new HashSet<>();
            Map<String, List<String>> classMethodsMap = new HashMap<>();

            new VoidVisitorAdapter<Void>() {
                @Override
                public void visit(ClassOrInterfaceDeclaration n, Void arg) {
                    super.visit(n, arg);
                    List<String> methodNames = new ArrayList<>();
                    for (MethodDeclaration m : n.getMethods()) {
                        methodNames.add(m.getNameAsString());
                        metrics.wmc++;
                        metrics.cyclomaticComplexity += calculateCyclomaticComplexity(m);
                    }
                    classMethodsMap.put(n.getNameAsString(), methodNames);
                    if (!n.getExtendedTypes().isEmpty()) {
                        metrics.dit++;
                    }
                    metrics.lcom += Math.max(0, methodNames.size() - 1);
                }

                @Override
                public void visit(MethodCallExpr n, Void arg) {
                    super.visit(n, arg);
                    calledMethods.add(n.getNameAsString());
                    n.getScope().ifPresent(s -> accessedTypes.add(s.toString()));
                }

                @Override
                public void visit(FieldDeclaration n, Void arg) {
                    super.visit(n, arg);
                    accessedTypes.add(n.getElementType().asString());
                }

                @Override
                public void visit(ObjectCreationExpr n, Void arg) {
                    super.visit(n, arg);
                    accessedTypes.add(n.getType().getNameAsString());
                }
            }.visit(cu, null);

            metrics.rfc = metrics.wmc + calledMethods.size();
            metrics.cbo = accessedTypes.size();

            metrics.externalInputs = (int) calledMethods.stream().filter(m -> m.toLowerCase().contains("set") || m.toLowerCase().contains("input")).count();
            metrics.externalOutputs = (int) calledMethods.stream().filter(m -> m.toLowerCase().contains("write") || m.toLowerCase().contains("print")).count();
            metrics.externalQueries = (int) calledMethods.stream().filter(m -> m.toLowerCase().contains("get") || m.toLowerCase().contains("query")).count();

            metrics.useCases = metrics.externalInputs + metrics.externalOutputs + metrics.externalQueries;
            metrics.useCasePoints = metrics.useCases + metrics.actors;
        }

        return metrics;
    }

    private static int calculateCyclomaticComplexity(MethodDeclaration method) {
        int complexity = 1;
        complexity += method.findAll(IfStmt.class).size();
        complexity += method.findAll(ForStmt.class).size();
        complexity += method.findAll(WhileStmt.class).size();
        complexity += method.findAll(SwitchEntry.class).size();
        return complexity;
    }
}
